package Map

import (
	"sanguo/cluster"
	codecs "sanguo/codec/cs"
	"sanguo/node/common/GlobalConstData"
	"sanguo/node/common/attr"
	"sanguo/node/common/transaction"
	"sanguo/node/node_game"
	"sanguo/node/node_game/module/role"
	"sanguo/node/node_game/user"
	table_Map "sanguo/node/table/excel/table/Map"
	cs_message "sanguo/protocol/cs/message"
	ss_rpc "sanguo/protocol/ss/rpc"
	"sanguo/rpc/enterMap"
	"sanguo/rpc/leaveMap"
	"time"

	"github.com/golang/protobuf/proto"
)

const (
	err_ok      = 1
	err_timeout = 2
	err_failed  = 3
)

type transactionEnterMap struct {
	transaction.TransactionBase
	user     *user.User
	req      *codecs.Message
	role     *role.Role
	errcode  int32
	node_map cluster.PeerID
}

func (this *transactionEnterMap) GetModuleName() string {
	return "Map"
}

func (this *transactionEnterMap) GetReplyMsg() proto.Message {
	return &cs_message.EnterMapToC{}
}

func (this *transactionEnterMap) SetExpiredTime(expired time.Time) {
	this.Expired = expired
}

func (this *transactionEnterMap) GetExpiredTime() time.Time {
	return this.Expired
}

func (this *transactionEnterMap) EnterMap(node_map cluster.PeerID) {
	var X, Y, Z, Angle int32
	req := this.req.GetData().(*cs_message.EnterMapToS)
	reqMap := req.GetMapID()
	if reqMap == 0 {
		reqMap = this.role.GetMapID()
	}
	if reqMap == 0 {
		//设置主城ID
		reqMap = GlobalConstData.MapID
	}
	if reqMap != this.role.GetMapID() {
		//需要根据地图ID获取进入点坐标
		mapDef := table_Map.Table_.IDMap[reqMap]
		X = mapDef.DefaultPosition.X
		Y = mapDef.DefaultPosition.Y
		Z = mapDef.DefaultPosition.Z
		Angle = mapDef.DefaultRotation
	} else {
		X, Y, Z = this.role.GetPos()
		Angle = this.role.GetAngle()
	}

	storyStage, _ := this.role.GetRoleAttr().GetAttr(attr.StoryStage)

	arg := &ss_rpc.EnterMapReq{
		MapType:    proto.Int32(reqMap),
		UserID:     proto.String(this.user.GetUserID()),
		Name:       proto.String(this.role.GetName()),
		ID:         proto.Uint64(this.role.GetID()),
		Lev:        proto.Int32(this.role.GetLev()),
		Portrait:   proto.Int32(this.role.GetPortrait()),
		Face:       proto.Int32(this.role.GetFace()),
		Hair:       proto.Int32(this.role.GetHair()),
		HairColor:  proto.Int32(this.role.GetHairColor()),
		EyeColor:   proto.Int32(this.role.GetEyeColor()),
		Cloth:      proto.Int32(this.role.GetCloth()),
		ClothColor: proto.Int32(this.role.GetClothColor()),
		Weapon1:    proto.Int32(this.role.GetWeapon1()),
		Weapon2:    proto.Int32(this.role.GetWeapon2()),
		Outline:    proto.Int32(this.role.GetOutline()),
		Game:       proto.String(node_game.GetSelfService().ToPeerID().ToString()),
		Pos: &ss_rpc.Position{
			X: proto.Int32(X),
			Y: proto.Int32(Y),
			Z: proto.Int32(Z),
		},
		Angle:      proto.Int32(Angle),
		StoryStage: proto.Int64(storyStage),
	}
	enterMap.AsynCall(node_map, arg, transaction.TransTimeoutMs, func(resp *ss_rpc.EnterMapResp, err error) {

		if !this.user.CheckTrans(this) {
			//trans已经结束
			return
		}
		if nil == err && resp.GetOk() == 1 {
			this.role.SetMapID(reqMap)
			this.role.SetPos(X, Y, Z)
			this.role.SetAngle(Angle)
			this.errcode = err_ok

			this.node_map = node_map
			this.role.NodeMap = &this.node_map
			this.role.EnterChat()

			this.user.EndTrans(this)
		} else {
			node_game.Debugln(this.role.GetUserID(), this.role.GetID(), "entermap", reqMap, "failed")
			this.errcode = err_failed
			this.user.EndTrans(this)
		}
	})
}

func (this *transactionEnterMap) Begin() {

	node_map, err := cluster.Random("map")
	if nil != err {
		node_game.Debugln("enterMap fail : no map server")
		this.errcode = err_failed
		this.user.EndTrans(this)
		return
	}

	roleModule := this.user.GetModuleByName("role").(*role.RoleModule)
	this.role = roleModule.CurrentRole()
	if nil == this.role || !this.role.IsRoleModleData() {
		node_game.Debugln(this.role.GetUserID(), "fail no selected role")
		this.errcode = err_failed
		this.user.EndTrans(this)
	} else {
		req := this.req.GetData().(*cs_message.EnterMapToS)
		reqMap := req.GetMapID()

		if this.role.NodeMap != nil {
			node_game.Debugln(this.role.GetUserID(), this.role.GetID(), "leave old map", this.role.GetMapID())
			arg := &ss_rpc.LeaveMapReq{
				MapType: proto.Int32(this.role.GetMapID()),
				UserID:  proto.String(this.user.GetUserID()),
			}
			leaveMap.AsynCall(*this.role.NodeMap, arg, transaction.TransTimeoutMs, func(resp *ss_rpc.LeaveMapResp, err error) {
				if !this.user.CheckTrans(this) {
					//trans已经结束
					return
				}
				if nil == err && resp.GetOk() == 1 {
					this.role.NodeMap = nil
					if reqMap != this.role.GetMapID() && this.role.GetMapID() != 0 {
						this.role.SetMapID(0)
					}
					this.EnterMap(node_map)
				} else {
					node_game.Debugln(this.role.GetUserID(), this.role.GetID(), "leave old map", this.role.GetMapID(), "failed")
					this.errcode = err_failed
					this.user.EndTrans(this)
				}
			})
		} else {
			this.EnterMap(node_map)
		}
	}
}

func (this *transactionEnterMap) End() {
	resp := &cs_message.EnterMapToC{}
	if this.errcode == err_ok {
		node_game.Infoln(this.role.GetUserID(), this.role.GetID(), "enterMap ok")
		resp.Errcode = cs_message.EnumType(cs_message.EnumType_OK).Enum()
		mapID := this.role.GetMapID()
		x, y, z := this.role.GetPos()

		resp.Pos = &cs_message.Position{
			X: proto.Int32(x),
			Y: proto.Int32(y),
			Z: proto.Int32(z),
		}
		resp.MapID = proto.Int32(mapID)
		resp.Angle = proto.Int32(this.role.GetAngle())

	} else {
		resp.Errcode = cs_message.EnumType(cs_message.EnumType_ERROR).Enum()
	}
	this.user.Reply(this.req.GetSeriNo(), resp)
}

func (this *transactionEnterMap) Timeout() {
	this.errcode = err_timeout
	this.user.EndTrans(this)
}

func init() {
	user.RegisterTransFunc(&cs_message.EnterMapToS{}, func(user *user.User, msg *codecs.Message) transaction.Transaction {
		return &transactionEnterMap{
			user: user,
			req:  msg,
		}
	})
}
